package knapsack.strategies;

import genetic.core.*;
import knapsack.core.KnapsackIndividual;
import knapsack.core.KnapsackInstance;
import knapsack.core.KnapsackStrategy;

import javax.swing.*;
import java.awt.*;
import java.util.Observable;

import static genetic.core.GeneticAlgorithmConstants.*;

/**
 * Evolutionary approach to the knapsack problem.
 *
 * @author vasek
 */
public final class EvolutionaryKnapsack extends Observable implements KnapsackStrategy {

    private Population population;
    private KnapsackIndividual best;
    private KnapsackInstance instance;

    /**
     * Constructor.
     *
     * @param instance instance to be solved
     */
    public EvolutionaryKnapsack(KnapsackInstance instance) {
        super();
        final Individual prototype = new KnapsackIndividual(new int[instance.getCount()], instance.getCosts(), instance.getWeights(), instance.getCapacity());
        final Selection selection = new TournamentSelection(MUTATION_PROBABILITY, CROSSOVER_PROBABILITY);
        this.population = new Population(POPULATION_SIZE, prototype, selection);
        this.instance = instance;
        if (BREED_MONITOR) {
            showBreedMonitor();
        }
    }

    @Override
    public void run() {
        for (int g = 0; g < MAX_GENERATIONS; g++) {
            best = (KnapsackIndividual) population.getBest();
            if (!best.isIdeal()) {
                population.nextGeneration();
                fireNextGenerationEvolved(g);
                g++;
            }
        }
    }

    /**
     * Notify observers that next generation has been evolved.
     *
     * @param generation current generation
     */
    private void fireNextGenerationEvolved(int generation) {
        final double fitness = best.getFitness();
        final PopulationChangedEvent event = new PopulationChangedEvent(generation, fitness);
        setChanged();
        notifyObservers(event);
    }

    @Override
    public int getKnapsackCost() {
        return (int) best.getFitness();
    }

    @Override
    public int[] getKnapsackConfiguration() {
        return ((KnapsackIndividual) best).getChromosome();
    }

    @Override
    public KnapsackInstance getInstance() {
        return instance;
    }

    @Override
    public int getNumberOfStatesExpanded() {
        return MAX_GENERATIONS * POPULATION_SIZE;
    }

    /**
     * Shows the {@link BreedMonitor} component during algorithm's processing.
     */
    private void showBreedMonitor() {
        final JFrame frame = new JFrame("Breed monitor");
        final Container container = frame.getContentPane();
        final BreedMonitor monitor = new BreedMonitor(MAX_GENERATIONS);
        container.add(monitor);
        addObserver(monitor);

        frame.setSize(640, 480);
        frame.setVisible(BREED_MONITOR);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
